from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_bcrypt import Bcrypt
from datetime import datetime
import os
from werkzeug.utils import secure_filename
from PIL import Image


# 在 app.py 开头添加
basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-12345'
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(basedir, "instance", "site.db")}'
app.config['UPLOAD_FOLDER'] = 'static/profile_pics'
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024  # 2MB限制

db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message_category = 'info'

# 允许的文件扩展名
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}


def allowed_file(filename):
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def save_picture(form_picture):
    # 生成随机文件名
    random_hex = os.urandom(8).hex()
    _, f_ext = os.path.splitext(form_picture.filename)
    picture_fn = random_hex + f_ext
    picture_path = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], picture_fn)

    # 调整图片大小并保存
    output_size = (125, 125)
    i = Image.open(form_picture)
    i.thumbnail(output_size)
    i.save(picture_path)

    return picture_fn


# 数据模型
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(60), nullable=False)
    image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
    posts = db.relationship('Post', backref='author', lazy=True)
    comments = db.relationship('Comment', backref='author', lazy=True)


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    comments = db.relationship('Comment', backref='post', lazy=True)


class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


# 路由
@app.route('/')
def index():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
    return render_template('index.html', posts=posts)


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        email = request.form.get('email')
        password = request.form.get('password')
        confirm_password = request.form.get('confirm_password')

        # 表单验证
        if not username or not email or not password:
            flash('所有字段都必须填写!', 'danger')
            return redirect(url_for('register'))

        if password != confirm_password:
            flash('两次输入的密码不一致!', 'danger')
            return redirect(url_for('register'))

        if len(password) < 6:
            flash('密码长度至少6位!', 'danger')
            return redirect(url_for('register'))

        # 检查用户是否已存在
        user_by_username = User.query.filter_by(username=username).first()
        if user_by_username:
            flash('用户名已存在!', 'danger')
            return redirect(url_for('register'))

        user_by_email = User.query.filter_by(email=email).first()
        if user_by_email:
            flash('邮箱已被注册!', 'danger')
            return redirect(url_for('register'))

        try:
            # 密码加密
            hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')

            # 创建新用户
            new_user = User(username=username, email=email, password=hashed_password)
            db.session.add(new_user)
            db.session.commit()

            flash('注册成功！请登录。', 'success')
            return redirect(url_for('login'))
        except Exception as e:
            db.session.rollback()
            flash('注册失败，请稍后重试。', 'danger')
            return redirect(url_for('register'))

    return render_template('register.html')


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        remember_me = bool(request.form.get('remember_me'))

        if not username or not password:
            flash('请填写用户名和密码!', 'danger')
            return redirect(url_for('login'))

        user = User.query.filter_by(username=username).first()

        # 验证用户存在且密码正确
        if user and bcrypt.check_password_hash(user.password, password):
            login_user(user, remember=remember_me)
            flash(f'欢迎回来, {username}!', 'success')

            # 重定向到用户原本想访问的页面，或者首页
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('index'))
        else:
            flash('登录失败，请检查用户名和密码。', 'danger')

    return render_template('login.html')


@app.route('/logout')
def logout():
    logout_user()
    flash('您已成功退出登录。', 'success')
    return redirect(url_for('index'))


@app.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
    if request.method == 'POST':
        title = request.form.get('title')
        content = request.form.get('content')

        if not title or not content:
            flash('标题和内容都不能为空!', 'danger')
            return redirect(url_for('new_post'))

        post = Post(title=title, content=content, author=current_user)
        db.session.add(post)
        db.session.commit()

        flash('文章发布成功！', 'success')
        return redirect(url_for('index'))

    return render_template('create_post.html')


@app.route('/post/<int:post_id>')
def post_detail(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('post_detail.html', post=post)


@app.route('/post/<int:post_id>/update', methods=['GET', 'POST'])
@login_required
def update_post(post_id):
    post = Post.query.get_or_404(post_id)

    if post.author != current_user:
        flash('你没有权限编辑这篇文章!', 'danger')
        return redirect(url_for('index'))

    if request.method == 'POST':
        title = request.form.get('title')
        content = request.form.get('content')

        if not title or not content:
            flash('标题和内容都不能为空!', 'danger')
            return redirect(url_for('update_post', post_id=post.id))

        post.title = title
        post.content = content
        db.session.commit()
        flash('文章更新成功!', 'success')
        return redirect(url_for('index'))

    return render_template('create_post.html', post=post, is_edit=True)


@app.route('/post/<int:post_id>/delete')
@login_required
def delete_post(post_id):
    post = Post.query.get_or_404(post_id)

    if post.author != current_user:
        flash('你没有权限删除这篇文章!', 'danger')
        return redirect(url_for('index'))

    db.session.delete(post)
    db.session.commit()
    flash('文章已删除!', 'success')
    return redirect(url_for('index'))


@app.route('/user/<username>')
def user_profile(username):
    user = User.query.filter_by(username=username).first_or_404()
    posts = Post.query.filter_by(author=user).order_by(Post.date_posted.desc()).all()
    post_count = len(posts)

    return render_template('user_profile.html',
                           user=user,
                           posts=posts,
                           post_count=post_count)


@app.route('/reset-password', methods=['GET', 'POST'])
@login_required
def reset_password():
    if request.method == 'POST':
        current_password = request.form.get('current_password')
        new_password = request.form.get('new_password')
        confirm_password = request.form.get('confirm_password')

        # 表单验证
        if not current_password or not new_password or not confirm_password:
            flash('所有字段都必须填写!', 'danger')
            return redirect(url_for('reset_password'))

        # 验证当前密码
        if not bcrypt.check_password_hash(current_user.password, current_password):
            flash('当前密码错误!', 'danger')
            return redirect(url_for('reset_password'))

        # 验证新密码一致性
        if new_password != confirm_password:
            flash('新密码不一致!', 'danger')
            return redirect(url_for('reset_password'))

        if len(new_password) < 6:
            flash('新密码长度至少6位!', 'danger')
            return redirect(url_for('reset_password'))

        try:
            # 更新密码
            hashed_password = bcrypt.generate_password_hash(new_password).decode('utf-8')
            current_user.password = hashed_password
            db.session.commit()

            flash('密码修改成功!', 'success')
            return redirect(url_for('user_profile', username=current_user.username))
        except Exception as e:
            db.session.rollback()
            flash('密码修改失败，请稍后重试。', 'danger')
            return redirect(url_for('reset_password'))

    return render_template('reset_password.html')


@app.route('/post/<int:post_id>/comment', methods=['POST'])
@login_required
def new_comment(post_id):
    post = Post.query.get_or_404(post_id)

    if request.method == 'POST':
        content = request.form.get('content')

        if not content:
            flash('评论内容不能为空!', 'danger')
            return redirect(url_for('post_detail', post_id=post.id))

        if len(content) > 500:
            flash('评论内容不能超过500字!', 'danger')
            return redirect(url_for('post_detail', post_id=post.id))

        # 创建新评论
        comment = Comment(
            content=content,
            author=current_user,
            post=post
        )

        db.session.add(comment)
        db.session.commit()

        flash('评论发布成功!', 'success')
        return redirect(url_for('post_detail', post_id=post.id))


@app.route('/comment/<int:comment_id>/delete')
@login_required
def delete_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)

    # 检查权限：评论作者或文章作者可以删除
    if comment.author != current_user and comment.post.author != current_user:
        flash('你没有权限删除这条评论!', 'danger')
        return redirect(url_for('post_detail', post_id=comment.post.id))

    post_id = comment.post.id
    db.session.delete(comment)
    db.session.commit()

    flash('评论已删除!', 'success')
    return redirect(url_for('post_detail', post_id=post_id))


@app.route('/search')
def search():
    query = request.args.get('q', '').strip()  # 获取搜索关键词并去除首尾空格

    page = request.args.get('page', 1, type=int)

    if query:
        # 在标题和内容中搜索关键词（不区分大小写）
        search_results = Post.query.filter(
            db.or_(
                Post.title.ilike(f'%{query}%'),
                Post.content.ilike(f'%{query}%')
            )
        ).order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)

        results_count = search_results.total
    else:
        # 如果没有搜索词，返回空结果
        search_results = []
        results_count = 0

    return render_template('search.html',
                           query=query,
                           posts=search_results,
                           results_count=results_count)


@app.route('/account', methods=['GET', 'POST'])
@login_required
def account():
    if request.method == 'POST':
        # 处理头像上传
        if 'picture' in request.files:
            file = request.files['picture']
            if file and file.filename != '' and allowed_file(file.filename):
                try:
                    # 删除旧头像（如果不是默认头像）
                    if current_user.image_file != 'default.jpg':
                        old_picture_path = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'],
                                                        current_user.image_file)
                        if os.path.exists(old_picture_path):
                            os.remove(old_picture_path)

                    # 保存新头像
                    picture_file = save_picture(file)
                    current_user.image_file = picture_file
                    flash('头像更新成功!', 'success')
                except Exception as e:
                    flash('头像上传失败，请重试。', 'danger')

        # 更新用户名和邮箱
        username = request.form.get('username')
        email = request.form.get('email')

        if username and username != current_user.username:
            # 检查用户名是否已存在
            existing_user = User.query.filter_by(username=username).first()
            if existing_user:
                flash('用户名已存在!', 'danger')
            else:
                current_user.username = username
                flash('用户名更新成功!', 'success')

        if email and email != current_user.email:
            # 检查邮箱是否已存在
            existing_email = User.query.filter_by(email=email).first()
            if existing_email:
                flash('邮箱已被使用!', 'danger')
            else:
                current_user.email = email
                flash('邮箱更新成功!', 'success')

        db.session.commit()
        return redirect(url_for('account'))

    return render_template('account.html')


if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True)